﻿using Blog.Application.DTO;
using Blog.Domain;
using Core.Auth;
using Core.Cache;
using Core.Common;
using Core.Common.EnumExtensions;
using Core.CPlatform;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace BlogWebApi
{
    public class Auth
    {
        /// <summary>
        /// 秘钥
        /// </summary>
        private const string SecurityKey = "BlogH123456789012";
        private const string issuer = "BlogAPI";
        private const string audience = "BlogUI";
        private const int expireDay = 7;
        public static string CreateToken(UserDTO user, string oldToken = "")
        {
            IList<Claim> claims = new List<Claim>()
                {
                    new Claim("account", user.Account),
                    new Claim("username", user.Username),
                    new Claim("sex", user.Sex),
                    new Claim("birthDate", user.BirthDate),
                    new Claim("email",user.Email),
                    new Claim("sign",user.Sign),
                    new Claim("phone",user.Phone),
                    new Claim("headPhoto",user.HeadPhoto),
                    new Claim("loginType",user.LoginType==0?"2":user.LoginType.GetEnumText()),
                    new Claim("expireDate",DateTime.Now.AddDays(expireDay).ToString())
                };
            var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(SecurityKey));
            var expires = DateTime.Now.AddMinutes(1);
            var token = new JwtSecurityToken(
                        issuer: issuer,
                        audience: audience,
                        claims: claims,
                        notBefore: DateTime.Now,
                        expires: expires,
                        signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature));
            string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
            ICacheFactory cacheFactory = ServiceLocator.Get<ICacheFactory>();
            ICacheClient cacheClient = cacheFactory.CreateClient(CacheType.Redis);
            if (!string.IsNullOrEmpty(oldToken))
                cacheClient.Remove(oldToken);
            cacheClient.StringSet(jwtToken, DateTime.Now.AddDays(expireDay).ToString(), TimeSpan.FromDays(expireDay));
            return jwtToken;
        }
        public static string ResolveToken(string token)
        {
            IJsonSerializer serializer = new JsonNetSerializer();
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            IJwtDecoder decoder = new JwtDecoder(serializer, urlEncoder);
            var json = decoder.Decode(token);
            return json;
        }
        public static UserDTO GetLoginUser()
        {
            IHttpContextAccessor httpContext = ServiceLocator.Get<IHttpContextAccessor>();
            ICacheFactory cacheFactory = ServiceLocator.Get<ICacheFactory>();
            ICacheClient cacheClient = cacheFactory.CreateClient(CacheType.Redis);
            bool flag = httpContext.HttpContext.Request.Headers.TryGetValue("loginToken", out StringValues token);
            if (!flag)
                throw new AuthException();
            if (token.Count>0&& token[0]=="null")
                throw new AuthException();
            string cacheValue = cacheClient.StringGet(token);
            if (string.IsNullOrEmpty(cacheValue))
                throw new AuthException();
            cacheValue = cacheValue.Replace("\"", "");
            DateTime expireTime = Convert.ToDateTime(cacheValue);
            if (DateTime.Now > expireTime)
            {
                cacheClient.Remove(token);
                throw new AuthException();
            }
            string json = ResolveToken(token);
            if (string.IsNullOrEmpty(json))
                throw new AuthException();
            dynamic d = JsonConvert.DeserializeObject(json);
            DateTime expireDate = d.expireDate;
            if (DateTime.Now > expireDate)
                throw new AuthException();
            UserDTO userDTO = ConvertToDTO(d);
            return userDTO;

        }
        /// <summary>
        /// 最后一天自动续期
        /// </summary>
        /// <param name="expireDate"></param>
        /// <param name="userDTO"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static string AutoAddExpire(string token)
        {
            try
            {
                string json = ResolveToken(token);
                dynamic d = JsonConvert.DeserializeObject(json);
                DateTime expireDate = d.expireDate;
                if (DateTime.Now >= expireDate.AddDays(-1) && DateTime.Now <= expireDate)
                {
                    return token = CreateToken(ConvertToDTO(d));
                }
                return null;
            }
            catch (Exception)
            {
                return "";
            }

        }
        public static void LoginOut()
        {
            IHttpContextAccessor httpContext = ServiceLocator.Get<IHttpContextAccessor>();
            ICacheFactory cacheFactory = ServiceLocator.Get<ICacheFactory>();
            ICacheClient cacheClient = cacheFactory.CreateClient(CacheType.Redis);
            bool flag = httpContext.HttpContext.Request.Headers.TryGetValue("loginToken", out StringValues token);
            if (flag)
                cacheClient.Remove(token);
        }
        private static UserDTO ConvertToDTO(dynamic d)
        {
            UserDTO userDTO = new UserDTO();
            userDTO.Account = d.account;
            userDTO.Username = d.username;
            userDTO.Sex = d.sex;
            userDTO.BirthDate = d.birthDate;
            userDTO.Email = d.email;
            userDTO.Sign = d.sign;
            userDTO.Phone = d.phone;
            userDTO.HeadPhoto = d.headPhoto;
            userDTO.LoginType = d.loginType;
            return userDTO;
        }
    }
}
